;	Internationally known as "KDE Mover-Sizer"							Version 1.3
;
;	http://corz.org/windows/software/accessories/KDE-resizing-moving-for-XP-or-Vista.php

;	Which is essentially..

;	Easy Window Dragging -- KDE style (requires XP/2k/NT) -- by Jonny
;	..with nobs on. See http://www.autohotkey.com and their forum.
;
;	This script makes it much easier to move or resize a window: 1) Hold down
;	the ALT key and LEFT-click anywhere inside a window to drag it to a new
;	location;	2) Hold down ALT and RIGHT-click-drag anywhere inside a window
;	to easily resize it; 3) Press ALT twice, but before releasing it the second
;	time, left-click to minimize the window under the mouse cursor, right-click
;	to maximize it, or middle-click to close it.
;
;	This script was inspired by and built on many like it in the forum. Thanks 
;	go out to ck, thinkstorm, Chris, and aurelian for a job well done.


;	Itstory:
;
;   October 12, 2009:	V3 icon! (the last one produced even more mails!). This one rocks. No complaints please!
;						You can now get straight to the KDE Mover-Sizer page from the About dialog.
;						Removed superfluous default AutoHotKey tray menu items.
;						Added an "Enable HotKeys" tray menu item, which toggles the HotKeys (it un/checks) 
;						Added an Exit menu item and simple exit function.
;						Added a menu option to get to the ini file, to hack at the things there's no menu item for.
;						All current prefs get written to the ini file so the user can see/set what's available.
;						Cleaned up documentation and web page, some. More to come. Maybe even comments!
;						Added about box text, some default prefs, tray, gui + menu fixes, other minor stuff.	~ Cor
;   October 10, 2009:	Added new algorithm for Snapping on Resize (Matthias Ihmig)
;						Added option for Restoring Window on Resize
;   October 4, 2009:	Added full support for multi screens (incl. snapping) (Matthias Ihmig)
;						Fixed Snapping on WorkArea (excluding task bar)
;						Added INI option for Snapping Distance and WinDelay
;						Added About box
;   October 3, 2009:	Added configuration file and option to enable&disable snapping (Matthias Ihmig)
;						Added snapping for (Alt-Left-Click) Moving Windows
;   June 16, 2009:		Added Vista Alt-Tab fix (by jordoex)
;						Added an information tip for the tray hover. Updated Icon (I noticed it 
;						clashed with a portable Linux I recently tried, so I created an original 
;						icon for KDE Mover-Sizer. ~ Cor
;   March    10, 2009:	Moving a maximized windows is now possible (First WinRestore to orig. size, then move)
;						Added: Alt+Middle Button maximizes/restores a window (Matthias Ihmig)
;   December 04, 2007:	Window snap-to-edge - just like KDE, but with extra fun!
;						Added Tray ToolTip help. ~ Cor
;   November 07, 2006:	Optimized resizing code in !RButton, courtesy of bluedawn.
;   February 05, 2006:	Fixed double-alt (the ~Alt hotkey) to work with latest versions of AHK.


;	Snap-To	Edges ..
;
;	If their edge comes within ten pixels of your desktop edge, the window snaps to it. 
;	Very neat;	it's what KDE does. But there's more..
;
;	If you keep mousing after the window snaps, you get a beautiful resizing control which
;	keeps on going. Also you can Alt-right-click any oversized windows and pop them straight back 
;	into the desktop. Note: If you are quick enough, you can break the snap when needed. 
;	Have fun! NOTE: You can now disable the right-click-to-snap behaviour, if preferred.
;
;	;o)
;
;	June 16, 2009:
;
;		Since giving this a page of its own, it's become insanely popular, 
;		and keeps finding its way onto those "five wee apps you can't live
;		without" type lists, which says a lot for the kind of software you
;		can have for yourself if you only rake about in the AutoIt and 
;		AutoHotKey forums once in a while.

;
;	NOTE: If your application wants the Alt key for hotkey modifiers, use Alt+Win+Key for that.
;	It's quite easy once you do it a few times, simply roll your thumb and finger on and off.



If (A_AhkVersion < "1.0.39.00")
{
    MsgBox,20,,This script may not work properly with your version of AutoHotkey. Continue?
    IfMsgBox,No
    ExitApp
}

Menu, tray, NoStandard

; Useful info on tray mouse hover.. ;o)
Menu,Tray,Tip,KDE Mover-Sizer.. `nAlt-Left-Click Windows to Move`nAlt-Right-Click Windows to Resize`n[right-click here for a menu] 

; Add option menu for Enabling/Disabling Snapping
Menu, MySpecialMenu, add, Magnetic Resizing, MenuSnapOnResizeMagnetic
Menu, MySpecialMenu, add, Resize Restores Maximized Windows, MenuDoRestoreOnResize
Menu, tray, add, About.., MenuAbout
Menu, tray, add
Menu, tray, add, Snap on Move, MenuSnapOnMoveHandler
Menu, tray, add, Snap on Resize, MenuSnapOnSizeHandler
Menu, tray, add
Menu, tray, add, Special.., :MySpecialMenu
Menu, tray, add, Edit My Ini File, EditMyIni
Menu, tray, add
Menu, tray, add, Enable HotKeys, HotKeysToggle
Menu, tray, add
Menu, tray, add, Exit, MenuExit



; read INI File
IniRead, SnapOnSizeEnabled, KDE_Mover-Sizer.ini, Settings, SnapOnSizeEnabled, 1   ; default: true
IniWrite, %SnapOnSizeEnabled%,   KDE_Mover-Sizer.ini, Settings, SnapOnSizeEnabled
IniRead, SnapOnMoveEnabled, KDE_Mover-Sizer.ini, Settings, SnapOnMoveEnabled, 1   ; default: true
IniWrite, %SnapOnMoveEnabled%,   KDE_Mover-Sizer.ini, Settings, SnapOnMoveEnabled
IniRead, SnapOnResizeMagnetic,  KDE_Mover-Sizer.ini, Settings, SnapOnResizeMagnetic, 1   ; default: true
IniWrite, %SnapOnResizeMagnetic%,   KDE_Mover-Sizer.ini, Settings, SnapOnResizeMagnetic
IniRead, DoRestoreOnResize,   KDE_Mover-Sizer.ini, Settings, DoRestoreOnResize,  1        ; default: true
IniWrite, %DoRestoreOnResize%,   KDE_Mover-Sizer.ini, Settings, DoRestoreOnResize
IniRead, SnappingDistance,      KDE_Mover-Sizer.ini, Settings, SnappingDistance, 10        ; default: 10 pixels
IniWrite, %SnappingDistance%,   KDE_Mover-Sizer.ini, Settings, SnappingDistance
; This is the setting that runs smoothest on my system. Depending on your video card and cpu power, 
; you may want to raise or lower this value..
IniRead, WinDelay,      KDE_Mover-Sizer.ini, Settings, WinDelay, 2
IniWrite, %WinDelay%,   KDE_Mover-Sizer.ini, Settings, WinDelay

; set initial "enabled" check..
menu, tray, Check, Enable HotKeys

if SnapOnMoveEnabled
    Menu, tray, ToggleCheck, Snap on Move
if SnapOnSizeEnabled
    Menu, tray, ToggleCheck, Snap on Resize
if SnapOnResizeMagnetic
    Menu, MySpecialMenu, ToggleCheck, Magnetic Resizing
if DoRestoreOnResize
    Menu, MySpecialMenu, ToggleCheck, Resize Restores Maximized Windows


SetWinDelay,%WinDelay%



CoordMode,Mouse,Screen

MayToggle := true
return

; toggle Snapping Special

MenuSnapOnMoveHandler:
Menu, tray, ToggleCheck, Snap on Move
SnapOnMoveEnabled := NOT SnapOnMoveEnabled
; save option to INI file in working directory
IniWrite, %SnapOnMoveEnabled%, KDE_Mover-Sizer.ini, Settings, SnapOnMoveEnabled
return

MenuSnapOnSizeHandler:
Menu, tray, ToggleCheck, Snap on Resize
SnapOnSizeEnabled := NOT SnapOnSizeEnabled
; save option to INI file in working directory
IniWrite, %SnapOnSizeEnabled%, KDE_Mover-Sizer.ini, Settings, SnapOnSizeEnabled
return

MenuSnapOnResizeMagnetic:
Menu, MySpecialMenu, ToggleCheck, Magnetic Resizing
SnapOnResizeMagnetic := NOT SnapOnResizeMagnetic
IniWrite, %SnapOnResizeMagnetic%, KDE_Mover-Sizer.ini, Settings, SnapOnResizeMagnetic
return

MenuDoRestoreOnResize:
Menu, MySpecialMenu, ToggleCheck, Resize Restores Maximized Windows
DoRestoreOnResize := NOT DoRestoreOnResize
IniWrite, %DoRestoreOnResize%, KDE_Mover-Sizer.ini, Settings, DoRestoreOnResize
return


EditMyIni:
Run, KDE_Mover-Sizer.ini
return

HotKeysToggle:
Menu,Tray,Icon,,,1
menu, tray, ToggleCheck,Enable HotKeys
Suspend
return

MenuExit:
ExitApp
return

MenuAbout:
MsgBox,4,About KDE Mover-Sizer..,
(
KDE Mover-Sizer..

   Version 1.3	(October, 2009)

KDE-Mover-Sizer (created with AutoHotKey: autohotkey.com)		    
makes it easy to move and resize windows without having 
to position your mouse cursor accurately; simply hold down
the Alt key, and click or drag anywhere on the window.

The shortcuts:

   Alt + Left Button  -> Drag to move a window.
   Alt + Right Button -> Drag to resize a window.
   Alt + Middle Button -> Maximize/Restore a window.

   Double-Alt + Left Button   -> Minimize a window.
   Double-Alt + Right Button  -> Maximize/Restore a window.  
   Double-Alt + Middle Button -> Close a window.

     The Double-Alt modifier is activated by pressing the
     Alt key twice, much like a double-click. Hold the second
     Alt-press down until you click the mouse button. Tada!

Known authors (in alphabetical order)..

   aurelian
   Chris
   ck
   Cor
   Jonny
   jordoex
   Matthias Ihmig
   thinkstorm

Do you wish to visit the KDE Mover-Sizer web page?
)
IfMsgBox No
    return
else IfMsgBox Yes
	Run, http://corz.org/windows/software/accessories/KDE-resizing-moving-for-XP-or-Vista.php
	return


; *********** MOVING WINDOW ***********
!LButton::
If DoubleAlt
{
    MouseGetPos,,,KDE_id
    ; This message is mostly equivalent to WinMinimize,
    ; but it avoids a bug with PSPad.
    PostMessage,0x112,0xf020,,,ahk_id %KDE_id%
    DoubleAlt := false
    return
}

; Vista+ Alt-Tab fix by jordoex..
IfWinActive ahk_class TaskSwitcherWnd
{
    Send {Blind}{LButton}
    return
}

; Get the initial mouse position and window id, and
; WinRestore if the window is maximized.
MouseGetPos,KDE_X1,KDE_Y1,KDE_id
WinGet,KDE_Win,MinMax,ahk_id %KDE_id%
If KDE_Win
{
    WinRestore,ahk_id %KDE_id%
    ; return    ; restore window size instead of abort
}

; Get the initial window position.
WinGetPos,KDE_WinX1,KDE_WinY1,KDE_WinW,KDE_WinH,ahk_id %KDE_id%
Loop
{
    GetKeyState,KDE_Button,LButton,P ; Break if button has been released.
    If KDE_Button = U
        break
    MouseGetPos,Mouse_X2,Mouse_Y2 ; Get the current mouse position.
    KDE_X2 := Mouse_X2
    KDE_Y2 := Mouse_Y2
    KDE_X2 -= KDE_X1    ; Obtain an offset from the initial mouse position.
    KDE_Y2 -= KDE_Y1

    KDE_WinX2 := (KDE_WinX1 + KDE_X2) ; Apply this offset to the window position.
    KDE_WinY2 := (KDE_WinY1 + KDE_Y2)

    ; get current screen boarders for snapping, do this within the loop to allow snapping an all monitors without releasing button
    GetCurrentScreenBoarders(CurrentScreenLeft, CurrentScreenRight, CurrentScreenTop, CurrentScreenBottom)

    if SnapOnMoveEnabled
    {
        if (KDE_WinX2 < CurrentScreenLeft + SnappingDistance) AND (KDE_WinX2 > CurrentScreenLeft - SnappingDistance)
            KDE_WinX2 := CurrentScreenLeft 

        if (KDE_WinY2 < CurrentScreenTop + SnappingDistance) AND (KDE_WinY2 > CurrentScreenTop - SnappingDistance)
            KDE_WinY2 := CurrentScreenTop

        if (KDE_WinX2 + KDE_WinW > CurrentScreenRight - SnappingDistance) AND (KDE_WinX2 + KDE_WinW < CurrentScreenRight + SnappingDistance)
            KDE_WinX2 := CurrentScreenRight - KDE_WinW

        if (KDE_WinY2 + KDE_WinH > CurrentScreenBottom - SnappingDistance) AND (KDE_WinY2 + KDE_WinH < CurrentScreenBottom + SnappingDistance)
            KDE_WinY2 := CurrentScreenBottom - KDE_WinH
    }

    WinMove,ahk_id %KDE_id%,,%KDE_WinX2%,%KDE_WinY2% ; Move the window to the new position.
}
return

; *********** RESIZING WINDOW ***********
!RButton::
If DoubleAlt
{
    MouseGetPos,,,KDE_id
    ; Toggle between maximized and restored state.
    WinGet,KDE_Win,MinMax,ahk_id %KDE_id%
    If KDE_Win
        WinRestore,ahk_id %KDE_id%
    Else
        WinMaximize,ahk_id %KDE_id%
    DoubleAlt := false
    return
}
; Get the initial mouse position and window id, and
; WinRestore if the window is maximized.
MouseGetPos,KDE_X1,KDE_Y1,KDE_id
WinGet,KDE_Win,MinMax,ahk_id %KDE_id%
If KDE_Win
{
    if DoRestoreOnResize
        WinRestore,ahk_id %KDE_id%
    else
    {
        GetCurrentScreenBoarders(CurrentScreenLeft, CurrentScreenRight, CurrentScreenTop, CurrentScreenBottom)
        WinRestore,ahk_id %KDE_id%
        WinMove,ahk_id %KDE_id%,, CurrentScreenLeft  ; X of resized window
                            , CurrentScreenTop       ; Y of resized window
                            , CurrentScreenRight  - CurrentScreenLeft ; W of resized window
                            , CurrentScreenBottom - CurrentScreenTop  ; H of resized window
    }
}

; Get the initial window position and size.
WinGetPos,KDE_WinX1,KDE_WinY1,KDE_WinW,KDE_WinH,ahk_id %KDE_id%
; Define the window region the mouse is currently in.
; The four regions are Up and Left, Up and Right, Down and Left, Down and Right.
If (KDE_X1 < KDE_WinX1 + KDE_WinW / 2)
   KDE_WinLeft := 1
Else
   KDE_WinLeft := -1
If (KDE_Y1 < KDE_WinY1 + KDE_WinH / 2)
   KDE_WinUp := 1
Else
   KDE_WinUp := -1
Loop

{
    GetKeyState,KDE_Button,RButton,P ; Break if button has been released.
    If KDE_Button = U
        break
    MouseGetPos,Mouse_X2,Mouse_Y2 ; Get the current mouse position.
    KDE_X2 := Mouse_X2
    KDE_Y2 := Mouse_Y2
    KDE_X2 -= KDE_X1 ; Obtain an offset from the initial mouse position.
    KDE_Y2 -= KDE_Y1

    ; snap the window to the edge of the screen if closer than 10 pixels to border
    ; first, get current screen boarders for snapping, do this within the loop to allow snapping an all monitors without releasing button
    ; get current screen boarders for snapping, do this within the loop to allow snapping an all monitors without releasing button
    GetCurrentScreenBoarders(CurrentScreenLeft, CurrentScreenRight, CurrentScreenTop, CurrentScreenBottom)

    if (SnapOnSizeEnabled AND NOT SnapOnResizeMagnetic)    ; "normal" resizing
    {
        KDE_WinX2 := (KDE_WinX1 + (KDE_WinLeft+1)/2*KDE_X2) ; X of resized windows
        KDE_WinY2 := (KDE_WinY1 +   (KDE_WinUp+1)/2*KDE_Y2) ; Y of resized windows
        KDE_WinW2 := (KDE_WinW  -     KDE_WinLeft  *KDE_X2) ; W of resized windows
        KDE_WinH2 := (KDE_WinH  -       KDE_WinUp  *KDE_Y2) ; H of resized windows

        if (KDE_WinX2 < CurrentScreenLeft + SnappingDistance) AND (KDE_WinX2 > CurrentScreenLeft - SnappingDistance) AND (KDE_WinLeft > 0) {
            KDE_WinW2 := KDE_WinW + KDE_WinX1 - CurrentScreenLeft
            KDE_WinX2 := CurrentScreenLeft
        }
        if (KDE_WinY2 < CurrentScreenTop + SnappingDistance) AND (KDE_WinY2 > CurrentScreenTop - SnappingDistance) AND (KDE_WinUp > 0) {
            KDE_WinH2 := KDE_WinH + KDE_WinY1 - CurrentScreenTop
            KDE_WinY2 := CurrentScreenTop
        }
        if (KDE_WinX2 + KDE_WinW2 > CurrentScreenRight - SnappingDistance) AND (KDE_WinX2 + KDE_WinW2 < CurrentScreenRight + SnappingDistance)  AND (KDE_WinLeft < 0) {
            KDE_WinW2 := - KDE_WinX1 + CurrentScreenRight
        }
        if (KDE_WinY2 + KDE_WinH2 > CurrentScreenBottom - SnappingDistance) AND (KDE_WinY2 + KDE_WinH2 < CurrentScreenBottom + SnappingDistance) AND (KDE_WinUp < 0) {
            KDE_WinH2 := - KDE_WinY1 + CurrentScreenBottom
        }
    }
    else if (SnapOnSizeEnabled AND SnapOnResizeMagnetic)    ;  Magnetic Edges resize the window but keep the edge "locked"
    {
        ; Get the current window position and size.
        WinGetPos,KDE_WinX1,KDE_WinY1,KDE_WinW,KDE_WinH,ahk_id %KDE_id%

        if (KDE_WinX1 < CurrentScreenLeft + SnappingDistance) ;AND (KDE_WinX1 > CurrentScreenLeft - SnappingDistance)
            KDE_WinX1 := CurrentScreenLeft

        if (KDE_WinY1 < CurrentScreenTop + SnappingDistance) ;AND (KDE_WinY1 > CurrentScreenTop - SnappingDistance)
            KDE_WinY1 := CurrentScreenTop

        if (KDE_WinX1 + KDE_WinW > CurrentScreenRight - SnappingDistance) ;AND (KDE_WinX1 + KDE_WinW < CurrentScreenRight + SnappingDistance)
            KDE_WinX1 := CurrentScreenRight - KDE_WinW

        if (KDE_WinY1 + KDE_WinH > CurrentScreenBottom - SnappingDistance) ;AND (KDE_WinY1 + KDE_WinH < CurrentScreenBottom + SnappingDistance)
            KDE_WinY1 := CurrentScreenBottom - KDE_WinH

        KDE_WinX2 := (KDE_WinX1 + (KDE_WinLeft+1)/2*KDE_X2) ; X of resized windows
        KDE_WinY2 := (KDE_WinY1 +   (KDE_WinUp+1)/2*KDE_Y2) ; Y of resized windows
        KDE_WinW2 := (KDE_WinW  -     KDE_WinLeft  *KDE_X2) ; W of resized windows
        KDE_WinH2 := (KDE_WinH  -       KDE_WinUp  *KDE_Y2) ; H of resized windows
 
        KDE_X1 := (KDE_X2 + KDE_X1) ; Reset the initial position for the next iteration.
        KDE_Y1 := (KDE_Y2 + KDE_Y1)
    }
    else    ; no snapping, just resizing
    {
        KDE_WinX2 := (KDE_WinX1 + (KDE_WinLeft+1)/2*KDE_X2) ; X of resized windows
        KDE_WinY2 := (KDE_WinY1 +   (KDE_WinUp+1)/2*KDE_Y2) ; Y of resized windows
        KDE_WinW2 := (KDE_WinW  -     KDE_WinLeft  *KDE_X2) ; W of resized windows
        KDE_WinH2 := (KDE_WinH  -       KDE_WinUp  *KDE_Y2) ; H of resized windows
    }

    ; Then, act according to the defined region.
    WinMove,ahk_id %KDE_id%,, KDE_WinX2  ; X of resized window
                            , KDE_WinY2  ; Y of resized window
                            , KDE_WinW2  ; W of resized window
                            , KDE_WinH2  ; H of resized window
}
return


; "Alt + MButton" may be simpler, but I
; like an extra measure of security for
; an operation like this.
!MButton::
If DoubleAlt
{
    MouseGetPos,,,KDE_id
    WinClose,ahk_id %KDE_id%
    DoubleAlt := false
    return
}

; Toggle window Maximize/Original size with Alt+Middle mouse button
If MayToggle
    MouseGetPos,,,KDE_id
    ; Toggle between maximized and restored state.
    WinGet,KDE_Win,MinMax,ahk_id %KDE_id%
    If KDE_Win
        WinRestore,ahk_id %KDE_id%
    Else
        WinMaximize,ahk_id %KDE_id%
    MayToggle := false
    return
return

!MButton Up::
    MayToggle := true
return


; This detects "double-clicks" of the alt key.
~Alt::
    DoubleAlt := A_PriorHotKey = "~Alt" AND A_TimeSincePriorHotkey < 400
    Sleep 0
    KeyWait Alt  ; This prevents the keyboard's auto-repeat feature from interfering.
return



; Function definitions

; get current screen boarders for monitor where mouse cursor is
GetCurrentScreenBoarders(ByRef CurrentScreenLeft, ByRef CurrentScreenRight, ByRef CurrentScreenTop, ByRef CurrentScreenBottom)
{
    ; get current screen boarders for snapping, do this within the loop to allow snapping an all monitors without releasing button
    MouseGetPos,Mouse_X,Mouse_Y
    SysGet, MonitorCount, MonitorCount
    Loop, %MonitorCount%
    {
        SysGet, MonitorWorkArea, MonitorWorkArea, %A_Index%
        if (Mouse_X >= MonitorWorkAreaLeft) AND (Mouse_X <= MonitorWorkAreaRight) AND (Mouse_Y >= MonitorWorkAreaTop) AND (Mouse_Y <= MonitorWorkAreaBottom)
        {
            CurrentScreenLeft   := MonitorWorkAreaLeft
            CurrentScreenRight  := MonitorWorkAreaRight
            CurrentScreenTop    := MonitorWorkAreaTop
            CurrentScreenBottom := MonitorWorkAreaBottom
        }
    }
}
